/* 
 * Copyright (c) 2009-2012, Newcastle University, UK.
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without 
 * modification, are permitted provided that the following conditions are met: 
 * 1. Redistributions of source code must retain the above copyright notice, 
 *    this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice, 
 *    this list of conditions and the following disclaimer in the documentation 
 *    and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
 * POSSIBILITY OF SUCH DAMAGE. 
 * KL 12-06-2011 
 * .C file for hardware specific functions like sleep, hibernate etc
 */
 
#include <Compiler.h>
#include "HardwareProfile.h"
#include "Analogue.h"
#include "myRTC.h"
#include <uart.h>
#include "USB_CDC.h"

#include "SystemProfile.h"
#include "Compiler.h"
#include "WirelessProtocols\Console.h"
#include "WirelessProtocols\LCDBlocking.h"
#include "TimeDelay.h"

// Globals

// Config settings
/*24 bit config 1 0b<unimp>0000<unimp>0000<res>0<jtag off>0<code prot off>1<prog writes on>1<debug off>1<res>1<EMUD1>11<wdtoff>0<stdrd wdt>1<unimp>0<wdt pres 32>1<wdt posc see table>****>*/ 
/* 
WDT bits
1111 = 1:32,768
1110 = 1:16,384
1101 = 1:8,192
1100 = 1:4,096
1011 = 1:2,048
1010 = 1:1,024
1001 = 1:512 <---
1000 = 1:256
0111 = 1:128
0110 = 1:64
0101 = 1:32
0100 = 1:16
0011 = 1:8
0010 = 1:4
0001 = 1:2
0000 = 1:1
*/
_CONFIG1(0b000000000011111101001010) 
/*24 bit config 2 0b<unimp>0000<unimp>0000<IESO off>0<plldiv2>001<plldis off>0<init osc = frc>000<clock switch en>01<no clk op>0<iolock on-off en>0<usb vreg off>1<res>1<prim osc off>11>*/
_CONFIG2(0b000000000001000001001111)
//    _CONFIG1( JTAGEN_OFF & GCP_OFF & GWRP_OFF & FWDTEN_OFF & ICS_PGx2) 
//    _CONFIG2( 0xF7FF & IESO_OFF & FCKSM_CSDCMD & OSCIOFNC_ON & POSCMOD_HS & FNOSC_FRCPLL & PLLDIV_DIV2 & IOL1WAY_ON)



// Write function for printf of c30 devices
__attribute__ ( (section(".libc"))) 
int write(int handle, void *buffer, unsigned int len)
{
	int i;
	
	/*
	handle 0 corresponds to stdout
	handle 1 corresponds to stdin
	handle 2 corresponds to stderr
	*/
	switch (handle)
	{
		case 0:
		case 1:
		case 2:
		default:
		for (i = len; i; --i)
		{
			/* change to write to some peripheral */
			//usb_putchar(*(char*)buffer++);
			while(BusyUART1()==TRUE);
			WriteUART1(*(char*)buffer++);
			
		}
	}
	return(len);
}


// Sleep mode - External peripherals all allowed to run unless specified
void SystemPwrSave(unsigned long NapSetting)
{
	unsigned int IEC0_save,IEC1_save,IEC2_save,IEC3_save,IEC4_save,IEC5_save; // Interrupt context saving

	asm("DISI #0x3FF"); // Stop interrupts

	// Pin levels
	//InitIO(); 		// Normal pin levels - all off - assume this has been called before
  	
	// External peripherals off as needed
	LDR_DISABLE();
	LED_INIT_PINS();
//	if (NapSetting & GYRO_POWER_DOWN)GyroStandby();
	if (NapSetting & ADC_POWER_DOWN)InitADCOff();
//	if (NapSetting & ACCEL_POWER_DOWN)AccelStandby();

	// Internal Peripherals off
	if (!NapSetting & WAKE_ON_TIMER1) T1CONbits.TON = 0;	// Timer1 is the ms count 
	/*Note: Add/remove power down settings as needed*/
	T2CONbits.TON = 0;
	T3CONbits.TON = 0;
	T4CONbits.TON = 0;
	T5CONbits.TON = 0;
	//SPI1STATbits.SPIEN = 0;	
	U1CONbits.USBEN = 0;
	U1PWRCbits.USBPWR = 0; // MCHP Bug, not cleared in usb detach +120uA
	//PMCONbits.PMPEN	= 0;

	/* Note: The power down bits of some modules actually increases power draw - do not use these (commented) */
	// ADD PMD register settings here for XLP power down

	// Reduce ICSP leakage
	TRISBbits.TRISB0 = 0; LATBbits.LATB0 = 0;
	TRISBbits.TRISB1 = 0; LATBbits.LATB1 = 0;

	if (NapSetting & JUST_POWER_DOWN_PERIPHERALS) // Just shuts down everything internal + selected externals
	{
		// Restore powered off peripherals here as well
		PMD4bits.USB1MD = 0;
		SRbits.IPL = 0b000; // Effectively re-enable ints - will vector!
		return;
	}

	// Disable all interrupts
	if (NapSetting & SAVE_INT_STATUS)
	{
		IEC0_save = IEC0;
		IEC1_save = IEC1;
		IEC2_save = IEC2;
		IEC3_save = IEC3;
		IEC4_save = IEC4;
		IEC5_save = IEC5;
	}
	IEC0 = IEC1 = IEC2 = IEC3 = IEC4 = IEC5 = 0; // Disable all ints

	// Enable wakeup interrupts + peripherals
	if (NapSetting==0){return;} // Stops it being bricked by accident
	if (NapSetting & WAKE_ON_USB)	// Wake on USB detect
	{
		SetupUSBsenseInts(); // Shared with button inter on change notifications
	}	 	
//	if (NapSetting & WAKE_ON_ADXL1)	// Wake on ADXL int1
//	{
//		ACCEL_INT1_IF = 0;
//		ACCEL_INT1_IE = 1;
//	}			
//	if (NapSetting & WAKE_ON_ADXL2)	// Wake on ADXL int2
//	{
//		ACCEL_INT2_IF = 0;
//		ACCEL_INT2_IE = 1;
//	}		 
//	if (NapSetting & WAKE_ON_GYRO1)	// Wake on GYRO int1
//	{
//		GY_INT1_IF = 0;
//		GY_INT1_IE = 1;
//	}			
//	if (NapSetting & WAKE_ON_GYRO2)	// Wake on GYRO int2
//	{
//		GY_INT2_IF = 0;
//		GY_INT2_IE = 1;
//	}		 	
	if (NapSetting & WAKE_ON_RTC)	// Wake on RTC alarm
	{
		TurnOnRTCInt();
	}		
	if (NapSetting & WAKE_ON_TIMER1)	// Timer1 is the ms count
	{
		IFS0bits.T1IF = 0;
		IEC0bits.T1IE = 0;
	}
	if (NapSetting & WAKE_ON_WDT) 	// Set period in config word 1 - currently 4096 * 1ms
		{ClrWdt();RCONbits.SWDTEN = 1;}	// Wake on WDT

	// Go to sleep... zzzzzzzzzzzzzz
	__builtin_write_OSCCONL(OSCCON & 0xFB); 	// Primary oscillator off during sleep (sleep will pll will incure wakeup penalty)
	REFOCON = 0;			// Reference oscillator off
	if(NapSetting & LOWER_PWR_SLOWER_WAKE)	RCONbits.PMSLP = 0; // Adds 64ms wakeup time
	else 									RCONbits.PMSLP = 1;
	Sleep();
	__builtin_nop();

	// ON RETURN FROM SLEEP 
	if ((NapSetting & WAKE_ON_WDT) &&(!(NapSetting & LEAVE_WDT_RUNNING)))
		{ClrWdt();RCONbits.SWDTEN = 0;}	// Turn off WDT

	// Restore the pins driven during sleep
	TRISBbits.TRISB0 = 1; 
	TRISBbits.TRISB1 = 1; 

	// Resore powered down modules here
	PMD4bits.USB1MD = 0;

	// Restore peripherals
	if (!(NapSetting & DONT_RESTORE_PERIPHERALS))
	{
		if (NapSetting & ADC_POWER_DOWN)InitADCOn();
//		if (NapSetting & GYRO_POWER_DOWN)GyroStartup();
//		if (NapSetting & ACCEL_POWER_DOWN)AccelStartup(ACCEL_DEFAULT_RATE);
	}

	// Restor Interrupts
	if (!(NapSetting & ALLOW_VECTOR_ON_WAKE))
	{
		IFS0 = IFS1 = IFS2 = IFS3 = IFS4 = IFS5 = 0; // Clear all flags
	}
	if (NapSetting & SAVE_INT_STATUS)
	{
		IEC0 = IEC0_save;
		IEC1 = IEC1_save; 
		IEC2 = IEC2_save; 
		IEC3 = IEC3_save; 
		IEC4 = IEC4_save; 
		IEC5 = IEC5_save;
	}
	asm("DISI #0");; // Effectively re-enable ints - will vector if flags are set!
    return ;                       
}


// Precharge -- startup code to cope with battery pre-charge state 
void WaitForPrecharge(void)
{
    unsigned int timer = 100;
	
	// Otherwise they could be drawing lots of power - n.b. no way to halt internal nand processes
	SystemPwrSave(JUST_POWER_DOWN_PERIPHERALS);

	// 32 kHz rc osc
	CLOCK_INTRC();
		
	InitADCOnRCOSC();
	SampleADC_noLDR(); // Get one sample
	InitADCOff();

	// if battery is flat and no charger/ USB present
	if ((ADCresult[0] < MINIMUM_SAFE_BATTERY_RUNNING_VOLTAGE)&&(!USB_BUS_SENSE))
	{
		// Stall here until charger/USB is connected - WDT reset will keep the device resetting/ checking the batt
		SystemPwrSave(WAKE_ON_WDT|LOWER_PWR_SLOWER_WAKE|WAKE_ON_USB|ADC_POWER_DOWN);
		LEDColour(ColourRed);
		Reset();
	}

	// Sample the battery
    for (;;)
    {
		ClrWdt();   // Reset WDT while we're in this loop
		timer++;

		// if battery is flat - red 5% flash @ ~2Hz
		if(timer>100)
		{
			LEDColour(ColourRed);
		}
		if (timer>105)			
		{
			LEDColour(ColourOff);
			// Check battery voltage
			InitADCOnRCOSC();
			SampleADC_noLDR(); 
			InitADCOff();
			if (ADCresult[0] > MINIMUM_SAFE_BATTERY_RUNNING_VOLTAGE)
			{
				// Not in precharge
				LEDColour(ColourOff);
				return;	
			}
			timer = 0;
		}
    }

return;    
}

void LEDColour (enum COLOUR colour)
{
if (colour & 0x1) LED_R = 1;	else LED_R = 0;
if (colour & 0x2) LED_G = 1;	else LED_G = 0;
if (colour & 0x4) LED_B = 1;	else LED_B = 0;
return;
}

//EOF
